#!/usr/bin/env python
#
# Copyright (C) 2006-2007 Async Open Source
#                         Henrique Romano <henrique@async.com.br>
#                         Johan Dahlin <jdahlin@async.com.br>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
""" A simple Gazpacho/Glade to GtkBuilder XML format converter. """
import sys
from xml.dom.minidom import Element
from xml.dom import minidom

class GtkBuilderConverter:
    def __init__(self):
        self._dom = None
	

    #
    # Public API
    #
	

    def parse_file(self, file):
        self._dom = minidom.parse(file)
	

    def parse_buffer(self, buffer):
        self._dom = minidom.parseString(buffer)
	

    def to_xml(self):
        return self._dom.toprettyxml("", "")
	

    def convert(self):
        if self._dom is None:
            raise ValueError("You must specify a buffer or filename")
	

        glade_iface = self._dom.getElementsByTagName("glade-interface")
        assert glade_iface, ("Bad formed XML, there is "
                             "no <glade-interface> tag.")
        glade_iface[0].tagName = 'interface'
        self._interface = glade_iface[0]
	

        # Strip requires
        requires = self._dom.getElementsByTagName("requires")
        for require in requires:
            require.parentNode.childNodes.remove(require)
	

        for node in self._dom.getElementsByTagName("widget"):
            node.tagName = "object"
            self._convert(node.getAttribute("class"), node)
        for node in self._dom.getElementsByTagName("ui"):
            self._convert_ui(node)
	

    #
    # Private
    #
	

    def _get_widgets_by_attr(self, doc, attr_name, attr_val, tagname="object"):
        return [widget for widget in doc.getElementsByTagName(tagname)
                    if widget.getAttribute(attr_name) == attr_val]
	

    def _get_widget(self, doc, name):
        """ It is helper method to get a widget given its ID. Remember that
        we can't use minidom's getElementById, since it requires special
        magic to be added in the XML header.
        """
        result = self._get_widgets_by_attr(doc, "id", name)
        if len(result) > 1:
            raise ValueError("It is not possible to have more than one "
                             "widget with the same id (`%s')" % name)
        elif len(result) == 1:
            return result[0]
        return None
	

    def _get_widgets_by_class(self, doc, klass):
        return self._get_widgets_by_attr(doc, "class", klass)
	

    def _packing_prop_to_child_attr(self, node, prop_name, prop_val,
                                    attr_name=None, attr_val=None):
        for child in node.getElementsByTagName("child"):
            packing_props = [p for p in child.childNodes if p.nodeName == "packing"]
            if not packing_props:
                continue
            assert len(packing_props) == 1
            packing_prop = packing_props[0]
            properties = packing_prop.getElementsByTagName("property")
            for prop in properties:
                if (prop.getAttribute("name") != prop_name
                    or prop.childNodes[0].data != prop_val):
                    continue
                packing_prop.removeChild(prop)
                child.setAttribute(attr_name or prop_name, attr_val or prop_val)
            if len(properties) == 1:
                child.removeChild(packing_prop)
	

    def _widget_prop_to_container_obj(self, node, prop, obj_class, obj_id,
                                      members_tag, member_tag, member_attrs):
        """ Given a widget property, move it to a container object specified
        by obj_class and obj_id parameters. Eg:
	

        prop = <property name='sizegroup'>sizegroup1</property>
	

        _widget_prop_to_container_obj(node, prop, 'GtkSizeGroup', 'sizegroup1',
                                      'widgets', 'widget', attrs)
	

        resultant XML:
	

        <object class='GtkSizeGroup' id='sizegroup1'>
          <widgets>
            <widget attrs/>
          </widgets>
        </object>
	

        Where 'attrs' is a list of 2-sized tuples,  with the first item being
        the attribute name and the last its value.
	

        The 'obj_id' parameter is only used if no nodes with attribute 'class'
        equals to obj_class is found in the document, otherwise the ID of the
        last node found will be used.
        """
        obj_name = prop.childNodes[0].data
        node.removeChild(prop)
        doc = node.ownerDocument
        obj = self._get_widget(doc, obj_name)
        if obj is None:
            widgets = self._get_widgets_by_class(doc, obj_class)
            if widgets:
                obj = widgets[-1]
            else:
                obj = doc.createElement("object")
                obj.setAttribute("class", obj_class)
                obj.setAttribute("id", obj_id)
                self._interface.insertBefore(
                    obj, self._interface.childNodes[0])
        members = obj.getElementsByTagName(members_tag)
        if not members:
            members = doc.createElement(members_tag)
            obj.appendChild(members)
        else:
            assert len(members) == 1
            members = members[0]
        member = doc.createElement(member_tag)
        for name, value in member_attrs:
            member.setAttribute(name, value)
        members.appendChild(member)
	

    def _convert_ui(self, node):
        cdata = node.childNodes[0]
        data = cdata.toxml().strip()
        if not data.startswith("<![CDATA[") or not data.endswith("]]>"):
            return
        data = data[9:-3]
        child = minidom.parseString(data).childNodes[0]
        nodes = child.childNodes[:]
        for child_node in nodes:
            node.appendChild(child_node)
        node.removeChild(cdata)
        if not node.hasAttribute("id"):
            return
        # Updating references made by widgets
        parent_id = node.parentNode.getAttribute("id")
        for widget in self._get_widgets_by_attr(node.ownerDocument,
                                                "constructor",
                                                node.getAttribute("id")):
            widget.getAttributeNode("constructor").value = parent_id
        node.removeAttribute("id")
	

    def _convert(self, klass, node):
        func = getattr(self, "_object_" + klass, None)
        if func:
            func(node)
        self._default_widget_converter(node)
	

    def _default_widget_converter(self, node):
        klass = node.getAttribute("class")
        for prop in node.getElementsByTagName("property"):
            if not prop.parentNode is node:
                continue
            prop_name = prop.getAttribute("name")
            if prop_name == "sizegroup":
                attrs = [("name", node.getAttribute("id")), ]
                self._widget_prop_to_container_obj(
                    node, prop, "GtkSizeGroup", "sizegroup1", "widgets",
                    "widget", attrs)
            elif prop_name == "tooltip" and not klass == "GtkAction":
                prop.setAttribute("name", "tooltip-text")
	

        for child in node.getElementsByTagName("accessibility"):
            child.parentNode.removeChild(child)
	

    def _object_GtkNotebook(self, node):
        self._packing_prop_to_child_attr(node, "type", "tab")
	

    def _object_GtkExpander(self, node):
        self._packing_prop_to_child_attr(node, "type", "label_item",
                                         attr_val="label")
	

    def _object_GtkFrame(self, node):
        self._packing_prop_to_child_attr(node, "type", "label_item",
                                         attr_val="label")
	

def test():
    doc = ("<glade-interface>"
           "  <widget class=\"GtkSizeGroup\" id=\"s1\">"
           "    <property name=\"mode\">horizontal</property>"
           "  </widget>"
           "  <widget class=\"GtkWindow\" id=\"window1\">"
           "    <child>"
           "      <widget class=\"GtkLabel\" id=\"label1\">"
           "        <property name=\"label\">Test</property>"
           "        <property name=\"sizegroup\">s1</property>"
           "        <property name=\"tooltip\">tooltip text</property>"
           "      </widget>"
           "    </child>"
           "  </widget>"
           "</glade-interface>")
    conv = GtkBuilderConverter()
    conv.parse_buffer(doc)
    conv.convert()
    print conv.to_xml()
	

def main():
    if not len(sys.argv) > 1:
        print "You must specify a filename"
        return
    conv = GtkBuilderConverter()
    conv.parse_file(sys.argv[1])
    conv.convert()
    print conv.to_xml()

if __name__ == "__main__":
	main()
